package device

import (
	"context"
	stdsql "database/sql"
	"encoding/json"
	"fmt"
	"strconv"
	"strings"

	"entgo.io/ent/dialect/sql"
	"gitee.com/wenchangshou/simple-admin-pancakebot-rpc/ent"
	"gitee.com/wenchangshou/simple-admin-pancakebot-rpc/internal/svc"
	"gitee.com/wenchangshou/simple-admin-pancakebot-rpc/internal/utils/dberrorhandler"
	"gitee.com/wenchangshou/simple-admin-pancakebot-rpc/types/pancakebot"
	"github.com/suyuan32/simple-admin-common/utils/pointy"
	"github.com/zeromicro/go-zero/core/logx"
)

type GetNearestDeviceLogic struct {
	ctx    context.Context
	svcCtx *svc.ServiceContext
	logx.Logger
}

func NewGetNearestDeviceLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetNearestDeviceLogic {
	return &GetNearestDeviceLogic{
		ctx:    ctx,
		svcCtx: svcCtx,
		Logger: logx.WithContext(ctx),
	}
}

func (l *GetNearestDeviceLogic) GetNearestDevice(in *pancakebot.LocationReq) (*pancakebot.GetNearestDeviceResp, error) {
	if in.Location == nil || *in.Location == "" {
		return nil, fmt.Errorf("location is required")
	}

	// 解析位置信息，支持多种格式
	longitude, latitude, err := parseLocation(*in.Location)
	if err != nil {
		return nil, fmt.Errorf("invalid location format: %w", err)
	}

	// 确定要返回的设备数量，默认为1
	limit := int64(1)
	if in.Total != nil && *in.Total > 0 {
		limit = *in.Total
	}

	// 创建一个结构体来接收查询结果（包括距离）
	type DeviceWithDistance struct {
		ID       uint64
		Distance float64
	}

	// 使用原生 SQL 查询最近的N个设备及其距离
	// 使用 geography 类型的 ST_Distance 来获取以米为单位的精确距离
	query := fmt.Sprintf(`
		SELECT id, 
		       ST_Distance(
		           location::geography, 
		           ST_SetSRID(ST_MakePoint(%f, %f), 4326)::geography
		       ) as distance
		FROM devices 
		WHERE deleted_at IS NULL
		ORDER BY location <-> ST_SetSRID(ST_MakePoint(%f, %f), 4326)
		LIMIT %d
	`, longitude, latitude, longitude, latitude, limit)

	rows, err := l.svcCtx.DB.Device.QueryContext(l.ctx, query)
	if err != nil {
		return nil, dberrorhandler.DefaultEntError(l.Logger, err, in)
	}
	defer func(rows *stdsql.Rows) {
		err := rows.Close()
		if err != nil {

		}
	}(rows)

	// 收集所有结果
	var results []DeviceWithDistance
	for rows.Next() {
		var result DeviceWithDistance
		if err := rows.Scan(&result.ID, &result.Distance); err != nil {
			return nil, dberrorhandler.DefaultEntError(l.Logger, err, in)
		}
		results = append(results, result)
	}

	if len(results) == 0 {
		return &pancakebot.GetNearestDeviceResp{
			Total: 0,
			Data:  []*pancakebot.DeviceInfo{},
		}, nil
	}

	// 批量查询所有设备的完整信息
	var deviceIDs []uint64
	distanceMap := make(map[uint64]float64) // 存储每个设备的距离
	for _, result := range results {
		deviceIDs = append(deviceIDs, result.ID)
		distanceMap[result.ID] = result.Distance
	}

	devices, err := l.svcCtx.DB.Device.Query().
		Where(func(s *sql.Selector) {
			s.Where(sql.In("id", convertToAny(deviceIDs)...))
		}).
		All(l.ctx)

	if err != nil {
		return nil, dberrorhandler.DefaultEntError(l.Logger, err, in)
	}

	// 构建响应数据
	var deviceInfos []*pancakebot.DeviceInfo

	// 创建一个 map 用于快速查找设备
	deviceMap := make(map[uint64]*ent.Device)
	for _, device := range devices {
		deviceMap[device.ID] = device
	}

	// 按照原始查询的顺序（距离排序）构建结果
	for _, result := range results {
		device, exists := deviceMap[result.ID]
		if !exists {
			continue
		}

		// 转换出餐口信息
		var outletPorts []*pancakebot.OutletPort
		for _, port := range device.OutletPorts {
			outletPorts = append(outletPorts, &pancakebot.OutletPort{
				Number: pointy.GetPointer(int64(port.Number)),
				Status: pointy.GetPointer(int64(port.Status)),
				InUse:  &port.InUse,
			})
		}

		// 转换 location 为字符串格式
		var locationStr string
		if device.Location != nil && device.Location.Geometry != nil {
			locationBytes, _ := json.Marshal(device.Location)
			locationStr = string(locationBytes)
		}

		deviceInfo := &pancakebot.DeviceInfo{
			Id:           &device.ID,
			CreatedAt:    pointy.GetPointer(device.CreatedAt.UnixMilli()),
			UpdatedAt:    pointy.GetPointer(device.UpdatedAt.UnixMilli()),
			DeviceName:   &device.DeviceName,
			DeviceNo:     pointy.GetPointer(int64(device.DeviceNo)),
			DeviceStatus: pointy.GetPointer(int64(device.DeviceStatus)),
			Address:      &device.Address,
			Ext:          &device.Ext,
			Type:         pointy.GetPointer(int64(device.Type)),
			Location:     &locationStr,
			OutletPort:   outletPorts,
			Distance:     pointy.GetPointer(result.Distance), // 添加距离信息（单位：米）
		}

		if device.LastHeartbeat != nil {
			deviceInfo.LastHeartbeat = pointy.GetPointer(device.LastHeartbeat.UnixMilli())
		}

		deviceInfos = append(deviceInfos, deviceInfo)
	}

	return &pancakebot.GetNearestDeviceResp{
		Total: uint64(len(deviceInfos)),
		Data:  deviceInfos,
	}, nil
}

// convertToAny 将 uint64 切片转换为 any 切片
func convertToAny(ids []uint64) []any {
	result := make([]any, len(ids))
	for i, id := range ids {
		result[i] = id
	}
	return result
}

// parseLocation 解析位置字符串，支持以下格式：
// 1. "经度,纬度" 例如: "121.5,31.2"
// 2. GeoJSON Point 格式: {"type":"Point","coordinates":[121.5,31.2]}
func parseLocation(location string) (longitude, latitude float64, err error) {
	location = strings.TrimSpace(location)

	// 尝试解析 GeoJSON 格式
	if strings.HasPrefix(location, "{") {
		var geoJSON struct {
			Type        string    `json:"type"`
			Coordinates []float64 `json:"coordinates"`
		}
		if err := json.Unmarshal([]byte(location), &geoJSON); err == nil {
			if geoJSON.Type == "Point" && len(geoJSON.Coordinates) >= 2 {
				return geoJSON.Coordinates[0], geoJSON.Coordinates[1], nil
			}
		}
	}

	// 尝试解析 "经度,纬度" 格式
	parts := strings.Split(location, ",")
	if len(parts) == 2 {
		lon, err1 := strconv.ParseFloat(strings.TrimSpace(parts[0]), 64)
		lat, err2 := strconv.ParseFloat(strings.TrimSpace(parts[1]), 64)
		if err1 == nil && err2 == nil {
			return lon, lat, nil
		}
	}

	return 0, 0, fmt.Errorf("unsupported location format, expected 'longitude,latitude' or GeoJSON Point")
}
